#ifndef LOADTT_HPP
#define LOADTT_HPP

#include <iostream>
#include <mockturtle/mockturtle.hpp>
#include <algorithm>
#include <vector>

namespace alice
{
	auto storett(std::string num)
	{
		std::string spec = num;
		auto size = spec.size();
		auto num_vars = 0;
		while (size >>= 1)
		{
			++num_vars;
		}
		kitty::dynamic_truth_table tt(num_vars);

		kitty::create_from_binary_string(tt, spec);
		return tt;
	}
	void printISOP(kitty::dynamic_truth_table tt)
	{

		const auto cubes = kitty::isop(tt);

		for (auto cube : cubes)
		{
			cube.print(tt.num_vars());
			std::cout << std::endl;
		}
	}
	void printCNF(kitty::dynamic_truth_table tt)
	{

		const auto cubes = kitty::cnf_characteristic(tt);

		for (auto cube : cubes)
		{
			// std::cout <<typeid(cube).name()<<std::endl;
			cube.print(tt.num_vars());
			std::cout << std::endl;
		}
	}

	class loadtt_command : public command
	{
	public:
		explicit loadtt_command(const environment::ptr &env) : command(env, "to convert binary to truth table")
		{
			add_option("--loadbinary, -b", binary_number, "load tt from binary");
			add_option("--isop, -i", binary_number, "print truth table in ISOP");
			add_option("--dsd, -d", binary_number, "dsd_decomposition");
			add_option("--function, -f", binary_number, "test tt to function");
			add_option("--implies, -m", binary_number, "test implies");
		}

	protected:
		void execute()
		{

			if (is_set("isop"))
			{
				printISOP(storett(binary_number));
			}
			else if (is_set("implies"))
			{

				std::vector<uint8_t> support;
				kitty::dynamic_truth_table tt = storett(binary_number);
				for (auto i = 0u; i < tt.num_vars(); ++i)
				{
					if (kitty::has_var(tt, i))
					{
						support.push_back(i);
					}
				}

				for (auto var : support)
				{

					auto tt2 = tt.construct();
					kitty::create_nth_var(tt2, var);
					if (implies(tt, tt2))
					{

						std::cout << "implies" << std::endl;
					}
					else
					{

						std::cout << "not implies" << std::endl;
					}
				}
			}
			else if (is_set("loadbinary"))
			{
				std::string spec = binary_number;
				auto size = spec.size();
				auto num_vars = 0;
				while (size >>= 1)
				{
					++num_vars;
				}
				kitty::dynamic_truth_table tt(num_vars);

				kitty::create_from_binary_string(tt, spec);
			}
			else if (is_set("function"))
			{
				kitty::dynamic_truth_table tt = storett(binary_number);
				std::vector<uint8_t> support;
				for (auto i = 0u; i < tt.num_vars(); ++i)
				{
					support.push_back(i);
				}
				std::cout << "begin" << std::endl;
				const auto co00 = cofactor0(tt, support[0]);
				const auto co01 = cofactor1(tt, support[0]);
				const auto dif0 = kitty::binary_xor(co00, co01);

				const auto difdif = kitty::cofactor0(kitty::cofactor0(dif0, support[1]), support[2]);
				if (kitty::is_const0(difdif))
				{
					std::cout << "equal 01 02" << std::endl;
				}

				const auto num_pairs = static_cast<uint32_t>(kitty::is_const0(difdif));
				if (num_pairs == 1u)
				{
					std::cout << "1u" << std::endl;
				}
				else
				{
					std::cout << "0u" << std::endl;
				}
			}
			else if (is_set("dsd"))
			{
				kitty::dynamic_truth_table table = storett(binary_number);
				mockturtle::klut_network ntk;

				const auto x1 = ntk.create_pi();
				const auto x2 = ntk.create_pi();
				const auto x3 = ntk.create_pi();
				const auto x4 = ntk.create_pi();
				auto fn = [&](kitty::dynamic_truth_table const &remainder, std::vector<mockturtle::klut_network::signal> const &children)
				{
					return ntk.create_node(children, remainder);
				};
				ntk.create_po(dsd_decomposition(ntk, table, {x1, x2, x3, x4}, fn));
				write_bench(ntk, std::cout);
				write_bench(ntk, "dsd.bench");
			}
		}

	private:
		std::string binary_number;
	};
	ALICE_ADD_COMMAND(loadtt, "truth_table");
}

#endif
